/*****************************************************************************/
/*!	\file		PIS1Callbacks.c
*	\brief 		C Source code file for the PIS10 Stack Example
	\par        Dalian Yunxing Tech Co., Ltd.

				Dalian, China
				Phone   : +86 (411) 8825 4852
				Email   : yx@yunxing.tech
*/
/*****************************************************************************/
#include "PIS10Callbacks.h"

#include <string.h>

/******************************************************************************
*       ReadCallbackHandler Function Definition
******************************************************************************/
/*!  \brief       Callback Function for when a Read has occurred
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptDataAttributeID	Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptReturnedValue		Pointer to the new value that generated the callback
 *
 *   \return        Returns an enum IEC61850_CallbackReturnServiceErrorCodes
 ******************************************************************************/
enum IEC61850_CallbackReturnServiceErrorCodes ReadCallbackHandler( void *ptUserData, const struct IEC61850_DataAttributeID *ptDataAttributeID, struct IEC61850_DataAttributeData *ptReturnedValue)
{
	enum IEC61850_CallbackReturnServiceErrorCodes ErrorCode = IEC61850_CB_ERROR_NONE; //Create return value and init to No Error

	return  ErrorCode;
}

/******************************************************************************
*       WriteCallbackHandler Function Definition
******************************************************************************/
/*!  \brief         Callback Function for when a Write has occurred
 *
 *    \ingroup       Callback Handlers
 *
 *    \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	  \param[in]	 ptDataAttributeID	Pointer to the data attribute ID that generated the callback
 *	  \param[in]	 ptReturnedValue	Pointer to the new value that generated the callback
 *
 *    \return        Returns an enum IEC61850_CallbackReturnServiceErrorCodes
 ******************************************************************************/
enum IEC61850_CallbackReturnServiceErrorCodes   WriteCallbackHandler(void * ptUserData, const struct IEC61850_DataAttributeID * ptDataAttributeID, const struct IEC61850_DataAttributeData * ptNewValue)
{
	enum IEC61850_CallbackReturnServiceErrorCodes ErrorCode = IEC61850_CB_ERROR_NONE; //Create return value and init to No Error
	if (ptDataAttributeID != NULL && ptNewValue != NULL && ptNewValue->pvData != NULL)
	{
		struct IEC61850_DataAttributeID_Generic* DaidGeneric = (struct IEC61850_DataAttributeID_Generic*)ptDataAttributeID;
		if ((DaidGeneric->uiField1 == 8) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0))
		{
			Integer32 newVal = *((Integer32*)ptNewValue->pvData);
			switch (DaidGeneric->uiField5)
			{
			case 1:
				SetMaxNumRcd(newVal);
				break;
			case 2:
				SetOpMod(newVal);
				break;
			case 3:
				SetMemFull(newVal);
				break;
			}
			PrintServerFullView();
		}
	}
	return ErrorCode;
}

/******************************************************************************
*       SelectCallbackHandler Function Definition
******************************************************************************/
/*!  \brief         Callback Function for when a Select has occurred
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       		Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptSelectValue		Pointer to the selected value that generated the callback
 *	 \param[in]	    iSyncroCheck		If non zero the control is to perform a Syncro check (if supported by control) prior to selection
 *	 \param[in]	    iInterlockCheck		If non zero the control is to perform a Interlock check (if supported by control) prior to selection
 *
 *   \return        Returns an enum IEC61850_CallbackReturnServiceErrorCodes
 ******************************************************************************/
enum eCommandAddCause  SelectCallbackHandler(void * ptUserData, const struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_DataAttributeData * ptSelectValue, const struct IEC61850_CommandParameters* ptSelectParameters)
{
	enum eCommandAddCause ErrorCode = IEC61850_COMMAND_ERROR_NONE; //Create return value and init to No Error

	return ErrorCode;
}


/******************************************************************************
*       OperateCallbackHandler Function Definition
******************************************************************************/
/*!  \brief         Callback Function for when an Operate has occurred
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       		Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptSelectValue		Pointer to the selected value that generated the callback
 *	 \param[in]	    iSyncroCheck		If non zero the control is to perform a Syncro check (if supported by control) prior to selection
 *	 \param[in]	    iInterlockCheck		If non zero the control is to perform a Interlock check (if supported by control) prior to selection
 *
 *   \return                  Returns an enum IEC61850_CallbackReturnServiceErrorCodes
 ******************************************************************************/
enum eCommandAddCause  OperateCallbackHandler(void* ptUserData, const struct IEC61850_DataAttributeID* ptControlID, const struct IEC61850_DataAttributeData* ptOperateValue, const struct IEC61850_CommandParameters* ptOperateParameters)
{
	enum eCommandAddCause ErrorCode = IEC61850_COMMAND_ERROR_NONE; //Create return value and init to No Error

	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;
	struct IEC61850_DataAttributeID_Generic* DaidGeneric = (struct IEC61850_DataAttributeID_Generic*)ptControlID;

	struct IEC61850_DataAttributeID_Generic StValDAID = { 0 }; //We use the Generic version as it is easier to assign the Data Attribute IDs
	struct IEC61850_DataAttributeData StValDAData = { 0 }; // Always initialize the structure to 0 to avoid Garbage data value
	IEC61850 myIEC61850Object = GetMyServerClient(); //Get the IEC61850 Structure
	Integer8 dbPosValue = DBPOS_OFF;
	Boolean boolValue = FALSE;

	/* ==== IMPORTANT NOTE ====
	 * We update the StVal related to the control in this callback
	 * This StVal should represent the hardware state and should be
	 * updated only when the hardware state changes
	 *
	 * This example does not have hardware attached so the StVal
	 * is updated when the operate happens to "emulate" a hardware change
	 * ========================
	 */

	if ((DaidGeneric->uiField1 == 1) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0))
	{/* CSWI */

		/* Setup the Data Attribute ID (DAID) Struct - We set this to the CtlVal of the Control*/
		StValDAID.Generic_type = IEC61850_DAID_GENERIC;
		StValDAID.uiField1 = 1;
		StValDAID.uiField2 = 0;
		StValDAID.uiField3 = 0;
		StValDAID.uiField4 = 0;
		StValDAID.uiField5 = 0;

		/* Setup the Data Attribute Data Struct */
		StValDAData.uiBitLength = IEC61850_DBPOS_BITSIZE; //sizeof(dbPosValue) * 8; //Set the size of the data in bits
		StValDAData.ucType = IEC61850_DATATYPE_DBPOS; //set the type of the data
		StValDAData.pvData = &dbPosValue; //Set a pointer to the data

		/* Set the DBPos Value based on the Operate's value to emulate hardware change */
		if ((*((Integer8*)ptOperateValue->pvData)) == FALSE)
		{
			dbPosValue = DBPOS_OFF;
		}
		else
		{
			dbPosValue = DBPOS_ON;
		}

		/* Perform the Update, note the IEC61850_DataAttributeID_Generic needs to be typecast to a standard struct IEC61850_DataAttributeID */
		eErrorCode = IEC61850_Update(myIEC61850Object, (struct IEC61850_DataAttributeID*)&StValDAID, &StValDAData, 1); //count of 1 as only 1 element is being updated
		if (eErrorCode == IEC61850_ERROR_NONE)
		{
			/* If the Update Succeeded Update the local value for the gui*/
			SetCSWIStVal((enum DbPosValues)dbPosValue);
			SetCSWICtlVal((*((Integer8*)ptOperateValue->pvData)));
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
		else
		{
			/* If the Update failed alert the user */
			char strError[SIZE_OF_ERROR_STRING] = { 0 };
			snprintf(strError, SIZE_OF_ERROR_STRING, "CSWI StVal: Update failed:(%i) %s\n", eErrorCode, IEC61850_ErrorString(eErrorCode));
			SetErrorString(strError, SIZE_OF_ERROR_STRING);
		}
	}
	else if ((DaidGeneric->uiField1 == 2) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0))
	{/* GGIO */

		/* Setup the Data Attribute ID (DAID) Struct - We set this to the CtlVal of the Control*/
		StValDAID.Generic_type = IEC61850_DAID_GENERIC;
		StValDAID.uiField1 = 2;
		StValDAID.uiField2 = 0;
		StValDAID.uiField3 = 0;
		StValDAID.uiField4 = 0;
		StValDAID.uiField5 = 0;

		/* Setup the Data Attribute Data Struct */
		StValDAData.uiBitLength = sizeof(boolValue) * 8; //Set the size of the data in bits
		StValDAData.ucType = IEC61850_DATATYPE_BOOLEAN; //set the type of the data
		StValDAData.pvData = &boolValue; //Set a pointer to the data

		/* Set the DBPos Value based on the Operate's value to emulate hardware change */
		boolValue = (*((Integer8*)ptOperateValue->pvData));

		/* Perform the Update, note the IEC61850_DataAttributeID_Generic needs to be typecast to a standard struct IEC61850_DataAttributeID */
		eErrorCode = IEC61850_Update(myIEC61850Object, (struct IEC61850_DataAttributeID*)&StValDAID, &StValDAData, 1); //count of 1 as only 1 element is being updated

		if (eErrorCode == IEC61850_ERROR_NONE)
		{
			/* If the Update Succeeded Update the local value for the gui*/
			SetGGIOStVal(boolValue);
			SetGGIOCtlVal(boolValue);
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
		else
		{
			/* If the Update failed alert the user */
			char strError[SIZE_OF_ERROR_STRING] = { 0 };
			snprintf(strError, SIZE_OF_ERROR_STRING, "GGIO StVal: Update failed:(%i) %s\n", eErrorCode, IEC61850_ErrorString(eErrorCode));
			SetErrorString(strError, SIZE_OF_ERROR_STRING);
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
	}
	else if ((DaidGeneric->uiField1 == 1) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 8) && (DaidGeneric->uiField5 == 0))
	{/* CSWI */

		/* Setup the Data Attribute ID (DAID) Struct - We set this to the CtlVal of the Control*/
		StValDAID.Generic_type = IEC61850_DAID_GENERIC;
		StValDAID.uiField1 = 1;
		StValDAID.uiField2 = 0;
		StValDAID.uiField3 = 0;
		StValDAID.uiField4 = 8;
		StValDAID.uiField5 = 0;

		/* Setup the Data Attribute Data Struct */
		StValDAData.uiBitLength = IEC61850_DBPOS_BITSIZE; //sizeof(dbPosValue) * 8; //Set the size of the data in bits
		StValDAData.ucType = IEC61850_DATATYPE_DBPOS; //set the type of the data
		StValDAData.pvData = &dbPosValue; //Set a pointer to the data

		/* Set the DBPos Value based on the Operate's value to emulate hardware change */
		if ((*((Integer8*)ptOperateValue->pvData)) == FALSE)
		{
			dbPosValue = DBPOS_OFF;
		}
		else
		{
			dbPosValue = DBPOS_ON;
		}

		/* Perform the Update, note the IEC61850_DataAttributeID_Generic needs to be typecast to a standard struct IEC61850_DataAttributeID */
		eErrorCode = IEC61850_Update(myIEC61850Object, (struct IEC61850_DataAttributeID*)&StValDAID, &StValDAData, 1); //count of 1 as only 1 element is being updated
		if (eErrorCode == IEC61850_ERROR_NONE)
		{
			/* If the Update Succeeded Update the local value for the gui*/
			SetCSWIStVal((enum DbPosValues)dbPosValue);
			SetCSWICtlVal((*((Integer8*)ptOperateValue->pvData)));
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
		else
		{
			/* If the Update failed alert the user */
			char strError[SIZE_OF_ERROR_STRING] = { 0 };
			snprintf(strError, SIZE_OF_ERROR_STRING, "CSWI StVal: Update failed:(%i) %s\n", eErrorCode, IEC61850_ErrorString(eErrorCode));
			SetErrorString(strError, SIZE_OF_ERROR_STRING);
		}
	}
	else if ((DaidGeneric->uiField1 == 2) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 8) && (DaidGeneric->uiField5 == 0))
	{/* GGIO */

		/* Setup the Data Attribute ID (DAID) Struct - We set this to the CtlVal of the Control*/
		StValDAID.Generic_type = IEC61850_DAID_GENERIC;
		StValDAID.uiField1 = 2;
		StValDAID.uiField2 = 0;
		StValDAID.uiField3 = 0;
		StValDAID.uiField4 = 8;
		StValDAID.uiField5 = 0;

		/* Setup the Data Attribute Data Struct */
		StValDAData.uiBitLength = sizeof(boolValue) * 8; //Set the size of the data in bits
		StValDAData.ucType = IEC61850_DATATYPE_BOOLEAN; //set the type of the data
		StValDAData.pvData = &boolValue; //Set a pointer to the data

		/* Set the DBPos Value based on the Operate's value to emulate hardware change */
		boolValue = (*((Integer8*)ptOperateValue->pvData));

		/* Perform the Update, note the IEC61850_DataAttributeID_Generic needs to be typecast to a standard struct IEC61850_DataAttributeID */
		eErrorCode = IEC61850_Update(myIEC61850Object, (struct IEC61850_DataAttributeID*)&StValDAID, &StValDAData, 1); //count of 1 as only 1 element is being updated

		if (eErrorCode == IEC61850_ERROR_NONE)
		{
			/* If the Update Succeeded Update the local value for the gui*/
			SetGGIOStVal(boolValue);
			SetGGIOCtlVal(boolValue);
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
		else
		{
			/* If the Update failed alert the user */
			char strError[SIZE_OF_ERROR_STRING] = { 0 };
			snprintf(strError, SIZE_OF_ERROR_STRING, "GGIO StVal: Update failed:(%i) %s\n", eErrorCode, IEC61850_ErrorString(eErrorCode));
			SetErrorString(strError, SIZE_OF_ERROR_STRING);
			/*Redraw Server GUI  */
			PrintServerFullView();
		}
	}

	return ErrorCode;
}

/******************************************************************************
*       CancelCallbackHandler Function Definition
******************************************************************************/
/*!  \brief       Callback Function for when a Cancel has occurred
 *
 *   \ingroup      Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *
 *   \return        Returns an enum IEC61850_CallbackReturnServiceErrorCodes
 ******************************************************************************/
enum eCommandAddCause  CancelCallbackHandler( void *ptUserData, const struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_CommandParameters* ptCancelParameters)
{
	enum eCommandAddCause ErrorCode = IEC61850_COMMAND_ERROR_NONE; //Create return value and init to No Error

	return ErrorCode;
}


/******************************************************************************
*       UpdateCallbackHandler Function Definition
******************************************************************************/
/*!  \brief      Callback Function for when an Update has occured
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptSelectValue		Pointer to the new value that has been sent
 *
 *   \return        None
 ******************************************************************************/
void  UpdateCallbackHandler( void *ptUserData, const struct IEC61850_DataAttributeID * ptUpdateID, const struct IEC61850_DataAttributeData *ptNewValue)
{
	struct IEC61850_DataAttributeID_Generic *DaidGeneric = (struct IEC61850_DataAttributeID_Generic *)ptUpdateID; //Type cast ptUpdateID to a generic ID

	Integer32 int32LocalVal =0; //Temporary variable to hold the current Local value
	Integer32 int32NewVal =0; //Temporary variable to hold the current Local value
	enum DbPosValues dbPosLocalValue = DBPOS_OFF;
	enum DbPosValues dbPosNewValue = DBPOS_OFF;
	enum IEC61850DbPosValues dbPoseNetworkValue = IEC61850_DB_POS_OFF; // The value from the callback uses IEC61850DbPosValues (hex values)

	Boolean boolLocalValue = FALSE;
	Boolean boolNewValue = FALSE;
	CrvPts crvPtsLocalValue = {0};
	Float32 XYValNewValue = 0;

	Float32 f32LocalValue = 0;
	Float32 f32NewValue = 0;

	if( (DaidGeneric->uiField1 == 1) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* CSWI StVal */
		dbPosLocalValue = GetCSWIStVal();
		dbPoseNetworkValue = (enum IEC61850DbPosValues)(*((Unsigned8*)ptNewValue->pvData));

		/*The DBPos values returned from the stack are of type enum IEC61850DbPosValues we need to do a conversion */
		switch(dbPoseNetworkValue)
		{
			case IEC61850_DB_POS_OFF:
				dbPosNewValue = DBPOS_INTERMEDIATE;
				break;
			case IEC61850_DB_POS_FALSE:
				dbPosNewValue = DBPOS_OFF;
				break;
			case IEC61850_DB_POS_TRUE:
				dbPosNewValue = DBPOS_ON;
				break;
			case IEC61850_DB_POS_INVALID:
				dbPosNewValue = DBPOS_BAD;
				break;
		}

		/* Check if the value has changed and if so update the local value and the GUI */
		if(dbPosLocalValue != dbPosNewValue)
		{
			SetCSWIStVal(dbPosNewValue);

			if(ptUserData != NULL) //if user data is set than this is a server subscription callback
			{
				SetErrorString("", 0);
				PrintServerSubscriptionFullView();
			}
			else
			{
				PrintClientFullView();
			}

		}
	}
	else if( (DaidGeneric->uiField1 == 1) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* CSWI CtlVal */
		boolLocalValue = GetCSWICtlVal();
		boolNewValue = (*((Integer8*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if((((boolLocalValue == FALSE) && (boolNewValue == FALSE)) || ((boolLocalValue != FALSE) && (boolNewValue != FALSE))) == FALSE)
		{
			SetCSWICtlVal(boolNewValue);
			PrintClientFullView();
		}
	}
	else if( (DaidGeneric->uiField1 == 2) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* GGIO StVal */
		boolLocalValue = GetGGIOStVal();
		boolNewValue = (*((Integer8*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if((((boolLocalValue == FALSE) && (boolNewValue == FALSE)) || ((boolLocalValue != FALSE) && (boolNewValue != FALSE))) == FALSE)
		{
			SetGGIOStVal(boolNewValue);
			if(ptUserData != NULL) //if user data is set than this is a server subscription callback
			{
				SetErrorString("", 0);
				PrintServerSubscriptionFullView();
			}
			else
			{
				PrintClientFullView();
			}
		}
	}
	else if( (DaidGeneric->uiField1 == 2) && (DaidGeneric->uiField2 == 1) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* GGIO CtlVal */
		boolLocalValue = GetGGIOCtlVal();
		boolNewValue = (*((Integer8*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if((((boolLocalValue == FALSE) && (boolNewValue == FALSE)) || ((boolLocalValue != FALSE) && (boolNewValue != FALSE))) == FALSE)
		{
			SetGGIOCtlVal(boolNewValue);
			PrintClientFullView();
		}

	}
	else if( (DaidGeneric->uiField1 == 3) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* MMXU PhsA Mag i */

		int32LocalVal = GetMMXUPhsAMagi();
		int32NewVal = (*((Integer32*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if(int32LocalVal != int32NewVal)
		{
			SetMMXUPhsAMagi(int32NewVal);
			PrintClientFullView();
		}
	}
	else if( (DaidGeneric->uiField1 == 4) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* MMXU PhsB Mag i */
		int32LocalVal = GetMMXUPhsBMagi();
		int32NewVal = (*((Integer32*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if(int32LocalVal != int32NewVal)
		{
			SetMMXUPhsBMagi(int32NewVal);
			PrintClientFullView();
		}
	}
	else if( (DaidGeneric->uiField1 == 5) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* MMXU PhsC Mag i */
		int32LocalVal = GetMMXUPhsCMagi();
		int32NewVal = (*((Integer32*)ptNewValue->pvData));

		/* Check if the value has changed and if so update the local value and the GUI */
		if(int32LocalVal != int32NewVal)
		{
			SetMMXUPhsCMagi(int32NewVal);
			PrintClientFullView();
		}
	}
	else if( (DaidGeneric->uiField1 == 6) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0) )
	{/* crvPts  */

		if(ptNewValue->iArrayIndex == -1) //If this is the entire Array
		{
			struct IEC61850_DataAttributeData *ptArrayXValElement = NULL;
			struct IEC61850_DataAttributeData *ptArrayYValElement = NULL;
			Integer8 LocalIndex =0;
			Boolean dataHasChanged = FALSE;

			/* loop through each pair of values in the array */
			int i =0;
			for(i=0; i < ((int)ptNewValue->uiBitLength); i=i+2)
			{
				LocalIndex = i/2;
				crvPtsLocalValue = GetPDIFCrvPts(LocalIndex);
				ptArrayXValElement = ((struct IEC61850_DataAttributeData *)ptNewValue->pvData)+i; //Get the Array xVal Element
				ptArrayYValElement = ((struct IEC61850_DataAttributeData *)ptNewValue->pvData)+(i+1); //Get the Array yVal Element

				/* Check if the xVal has a new value and if so update the local xVal value and set the dataHasChanged flag */
				XYValNewValue = (*(Float32*)ptArrayXValElement->pvData);
				/* Floating points are never 100% accurate on systems so we use the difference method to determine if it is close enough */
				if( fabs(crvPtsLocalValue.xVal - XYValNewValue) > 0.00000001)
				{
					crvPtsLocalValue.xVal = XYValNewValue;
					SetPDIFCrvPts(LocalIndex, crvPtsLocalValue);
					dataHasChanged = TRUE;
				}

				/* Check if the yVal has a new value and if so update the local yVal value and set the dataHasChanged flag */
				XYValNewValue = (*(Float32*)ptArrayYValElement->pvData);
				/* Floating points are never 100% accurate on systems so we use the difference method to determine if it is close enough */
				if( fabs(crvPtsLocalValue.xVal - XYValNewValue) > 0.00000001)
				{
					crvPtsLocalValue.yVal = XYValNewValue;
					SetPDIFCrvPts(LocalIndex, crvPtsLocalValue);
					dataHasChanged = TRUE;
				}
			}

			/* if the data has changed flag is TRUE (not FALSE) then update the GUI */
			if(dataHasChanged != FALSE)
			{
				PrintClientFullView();
			}
		}
	}
	else if ((DaidGeneric->uiField1 == 7) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 0))
	{	/* GGIO Anin1 Mag f */


	f32LocalValue = GetTTNSTnsSv1Magf();

	f32NewValue = (*((Float32*)ptNewValue->pvData));

	/* Check if the value has changed and if so update the local value and the GUI */
	if (f32LocalValue != f32NewValue)
	{
		SetTTNSTnsSv1Magf(f32NewValue);
		PrintClientFullView();
	}
	}
	else if ((DaidGeneric->uiField1 == 7) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 1))
	{	/* GGIO Anin2 Mag f */


	f32LocalValue = GetTTNSTnsSv2Magf();

	f32NewValue = (*((Float32*)ptNewValue->pvData));

	/* Check if the value has changed and if so update the local value and the GUI */
	if (f32LocalValue != f32NewValue)
	{
		SetTTNSTnsSv2Magf(f32NewValue);
		PrintClientFullView();
	}
	}
	else if ((DaidGeneric->uiField1 == 7) && (DaidGeneric->uiField2 == 0) && (DaidGeneric->uiField3 == 0) && (DaidGeneric->uiField4 == 0) && (DaidGeneric->uiField5 == 2))
	{	/* GGIO Anin3 Mag f */


	f32LocalValue = GetTTNSTnsSv3Magf();

	f32NewValue = (*((Float32*)ptNewValue->pvData));

	/* Check if the value has changed and if so update the local value and the GUI */
	if (f32LocalValue != f32NewValue)
	{
		SetTTNSTnsSv3Magf(f32NewValue);
		PrintClientFullView();
	}
	}
}

/******************************************************************************
*       ErrorCallbackHandler Function Definition
******************************************************************************/
/*!  \brief       Function that is called for an error callback
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptErrorParamtrs		Pointer to the struct IEC61850_ErrorParameters
 *
 *   \return        None
 *
 ******************************************************************************/
enum eCommandAddCause ErrorCallbackHandler(void * ptUserData, const struct IEC61850_DataAttributeID * ptDataAttributeID, const struct IEC61850_ErrorParameters * ptErrorParamtrs)
{
	enum eCommandAddCause eError = IEC61850_COMMAND_ERROR_NONE;

	//printf("\n========= Error Callback  =======\n");
	//printf("\t The error for %s/%s \n", ptErrorParamtrs->uDomainName, ptErrorParamtrs->uItemName);
	//printf("\t The cause is %s and type %s \n", ptErrorParamtrs->uErrorCause, ptErrorParamtrs->uErrorType);
	//printf("\t The DAID is: ");
	//IEC61850_PrintDataAttributeID(ptDataAttributeID);
	//printf("\n");

	return eError;
};

/******************************************************************************
*       OperativeTestCallbackHandler Function Definition
******************************************************************************/
/*!  \brief     Function that is called for an Operative test  callback
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptOperativeTestParameters		Pointer to the struct IEC61850_CommandParameters
 *
 *   \return  None
 ******************************************************************************/
enum eCommandAddCause OperativeTestCallbackHandler(void * ptUserData, const struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_CommandParameters* ptOperativeTestParameters)
{
	enum eCommandAddCause eError = IEC61850_COMMAND_ERROR_NONE;

	//printf("\n========= Operative Test Callback  =======\n");
	//printf("\t The DAID is: ");
	//IEC61850_PrintDataAttributeID(ptControlID);
	//printf("\n");

	return eError;
}


/******************************************************************************
*       QuestionableCallbackHandler Function Definition
******************************************************************************/
/*!  \brief       Function that is called for a Questionable data point callback
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptDataAttributeID	Pointer to the data attribute ID that generated the callback
 *
 *   \return  None
 ******************************************************************************/
void QuestionableCallbackHandler(void * ptUserData, const struct IEC61850_DataAttributeID * ptDataAttributeID)
{

	//printf("\n========= Questionable Data Point Callback =======\n");
	//printf("\t The DAID is: ");
	//IEC61850_PrintDataAttributeID(ptDataAttributeID);
	//printf("\n");
	if(ptUserData != NULL)  //If the data is not NULL then this is a server subscriber
	{
		struct IEC61850_DataAttributeID_Generic * ptGeneric = (struct IEC61850_DataAttributeID_Generic *)ptDataAttributeID;

		/* If the Data point is questionable alert the user */
		char strError[SIZE_OF_ERROR_STRING] = {0};
		snprintf(strError, SIZE_OF_ERROR_STRING, "GOOSE Data point is questionable - Private ID: %u, %u, %u, %u, %u", ptGeneric->uiField1, ptGeneric->uiField2, ptGeneric->uiField3, ptGeneric->uiField4, ptGeneric->uiField5);
		SetErrorString(strError, SIZE_OF_ERROR_STRING);

		PrintServerSubscriptionFullView(); // update server subscriber screen
	}
}


/******************************************************************************
*       CommandTerminationCallback Function Definition
******************************************************************************/
/*!  \brief       Function that is called for a Command Termination callback
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    ptControlID			Pointer to the data attribute ID that generated the callback
 *	 \param[in]	    ptCmdTermValue		Pointer to the value that has been sent
 *
 *   \return  None
 ******************************************************************************/
enum eCommandAddCause CommandTerminationCallback(void * ptUserData, const struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_DataAttributeData * ptCmdTermValue)
{
	enum eCommandAddCause eError = IEC61850_COMMAND_ERROR_NONE;

	//printf("\n========= Command Termination Callback =======\n");
	//printf("\t The DAID is: ");
	//IEC61850_PrintDataAttributeID(ptControlID);
	//printf("\n");

	return eError;
}




/******************************************************************************
*       FileCallbackHandler Function Definition
******************************************************************************/
/*!  \brief       File callback definition. Called when a file operation is requested.
 *                If this callback is not defined by the application, file reads and read attributes are permitted on all files,
 *                and file deletion is rejected with error code ACCESS_DENIED.
 *
 *   \ingroup       Callback Handlers
 *
 *   \param[in]     ptUserData       	Pointer to the user data memory declared during creation
 *	 \param[in]	    fileCallType    	The type of file call requested
 *	 \param[in]	    ptFileAttributes	The information about the file(s) in question
 *
 *   \return  enum IEC61850_FileResponseCode
 ******************************************************************************/


enum IEC61850_FileResponseCode FileCallbackHandler(void * ptUserData, enum IEC61850_FileCallType fileCallType, struct tFileAttr* ptFileAttributes)
{
    enum IEC61850_FileResponseCode responseCode = IEC61850_FILE_RESPONSE_ACCEPT;
    Unsigned32 fileIndex = 0;
    
    switch(fileCallType)
    {
        case IEC61850_FILE_CALL_READ:    
            //  printf("Open file: %s\n", ptFileAttributes->primaryFile.cFileName);
             break;
        case IEC61850_FILE_CALL_GET_ATTRIBUTES:     
            //  printf("Read file or directory attributes of '%s'\n", ptFileAttributes->primaryFile.cFileName);
             if(strcmp(ptFileAttributes->primaryFile.cFileName, "MySecretDirectory") == 0)
             {
                        responseCode = IEC61850_FILE_RESPONSE_ACCESS_DENIED;
             }
             else
             {
                for(fileIndex = 0; fileIndex < ptFileAttributes->u32NumOfDirectoryEntries; fileIndex++)
                {
                     struct tFile * pFile = &ptFileAttributes->ptArrayofDirectoryEntries[fileIndex];
                     // Example of hiding files: Hide everything starting with P
                     if(pFile->cFileName[0] == 'P')
                     {
                                pFile->bHide = TRUE;
                     }
                     // Print information
                 //     printf(" Entry %4u %c Time %-20s Size %-7u  File %s\n", fileIndex, pFile->bHide ? 'H' : ' ', pFile->ctLastModified, pFile->u32FileSize, pFile->cFileName);
                }
             }
             break;
        case IEC61850_FILE_CALL_DELETE:  
             // Set to IEC61850_FILE_RESPONSE_ACCEPT to allow file delete, set back to IEC61850_FILE_RESPONSE_ACCESS_DENIED  to block deleting files,
			 //responseCode = IEC61850_FILE_RESPONSE_ACCESS_DENIED;

             //if(responseCode == IEC61850_FILE_RESPONSE_ACCESS_DENIED)
             //{
               //  printf("Delete file '%s' denied\n", ptFileAttributes->primaryFile.cFileName);
             //}
             //else if(responseCode == IEC61850_FILE_RESPONSE_ACCEPT)
             //{
               //  printf("Delete file '%s' allowed \n", ptFileAttributes->primaryFile.cFileName);
             //}
             //else
             //{
               //  printf("Delete file '%s' - File does not exist \n", ptFileAttributes->primaryFile.cFileName);
             //}

             break;
        case IEC61850_FILE_CALL_SET_FILE:           
             // printf("Obtain file from the client. Local file '%s', remote file '%s'\n", ptFileAttributes->primaryFile.cFileName, ptFileAttributes->ptArrayofDirectoryEntries[0].cFileName);
             break;
        default:
             // printf("Unknown file call type %d\n", fileCallType);
             break;
    }
    return responseCode;
}
